// Copyright 2022 jeff.li. and/or its affiliates.

#pragma once

#include <cmath>
#include <cstdlib>
#include <limits>
#include <type_traits>

#include <tbir/runtime/runtime_port.h>

namespace tbir::runtime::floating_point {

    template<class T>
    TURBO_FORCE_INLINE static
    typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type
    internal_almost_equals(T x, T y, int ulp) {
        // the machine epsilon has to be scaled to the magnitude of the values used
        // and multiplied by the desired precision in ULPs (units in the last place)
        return std::fabs(x - y) <= std::numeric_limits<T>::epsilon() * std::fabs(x + y) * ulp
               // unless the result is subnormal
               || std::fabs(x - y) < std::numeric_limits<T>::min();
    }

    template<typename T1,
            typename T2,
            typename = typename std::enable_if<std::is_floating_point<T1>::value &&
                                               std::is_floating_point<T2>::value>::type>
    TURBO_FORCE_INLINE static bool AlmostEquals(const T1 &lhs, const T2 &rhs) {
        using Float1 = typename std::remove_cv<typename std::remove_reference<T1>::type>::type;
        using Float2 = typename std::remove_cv<typename std::remove_reference<T2>::type>::type;
        using Float = typename std::conditional<(sizeof(Float1) > sizeof(Float2)), Float1, Float2>::type;
        return internal_almost_equals<Float>(lhs, rhs, 4);
    }

}  // namespace tbir::runtime::floating_point
